
#include <stdio.h>
#include <string.h>
#include "gpio.h"
#include "uart.h"
#include "spi.h"
#include "flash.h"
#include "device.h"

extern devItem_t __devRegTable_start[];
extern devItem_t __devRegTable_end[];

static void devRegTableSplitArray(devItem_t* arr, int low, int high, int *p ) 
{ 
    int i, j;
    int item;
    devItem_t t;

    item = (int)arr[low].class;
    i = low;
    j = high;

    while( i < j )
    {
        /*  move from R to L in search of element < item */
        while ( (int)arr[j].class> item )
            j = j - 1;

        /*  move from L to R in search of element > item */
        while ( (int)arr[i].class<= item  &&  i < j )
            i = i + 1;

        if ( i < j )
        {
            t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
    }
    *p = j;
    t = arr[low];
    arr[low] = arr[*p];
    arr[*p] = t;
}

static void devRegTableQuickSort(devItem_t* arr, int low, int high )
{
    int pos;

    if( low < high )
    {
        devRegTableSplitArray( arr, low, high, &pos );
        devRegTableQuickSort( arr, low, pos - 1 );
        devRegTableQuickSort( arr, pos + 1, high );
    }
}

int device_init(void) {
    uint32_t devItemNum = __devRegTable_end - __devRegTable_start;
    devItem_t * devRegTable = __devRegTable_start;
    uint32_t itemIdx = 0;

    if(devItemNum)
        devRegTableQuickSort(devRegTable, 0, devItemNum - 1);

    for(; itemIdx < devItemNum; itemIdx++) {
        switch(devRegTable[itemIdx].class) {
        #if CONFIG_GPIO
            case DEV_CLASS_GPIO: {
                devRegTable[itemIdx].handle = (int)gpio_init((gpio_pin_e)devRegTable[itemIdx].port);
                if(!devRegTable[itemIdx].handle) return -1;
                break;
            }
        #endif
        #if CONFIG_UART
            case DEV_CLASS_UART: {
                devRegTable[itemIdx].handle = (int)uart_init((uart_port_e)devRegTable[itemIdx].port);
                if(!devRegTable[itemIdx].handle) return -1;
                break;
            }
        #endif
        #if CONFIG_SPI
            case DEV_CLASS_SPI: {
                devRegTable[itemIdx].handle = (int)spi_init((spi_port_e)devRegTable[itemIdx].port);
                if(!devRegTable[itemIdx].handle) return -1;
                break;
            }
        #endif
        #if CONFIG_FLASH
            case DEV_CLASS_FLASH: {
                devRegTable[itemIdx].handle = (int)flash_init((flash_type_e)devRegTable[itemIdx].port);
                if(!devRegTable[itemIdx].handle) return -1;
                break;
            }
        #endif

            default: return -1;
        }
    }

    return 0;
}

int device_find_by_name(char *name) {
    uint32_t devItemNum = __devRegTable_end - __devRegTable_start;
    devItem_t * devRegTable = __devRegTable_start;
    uint32_t itemIdx = 0;

    for(; itemIdx < devItemNum; itemIdx++) {
        if(strcasecmp(devRegTable[itemIdx].name, name) == 0)
            return devRegTable[itemIdx].handle;
    }

    return 0;
}

int device_find_by_class_port(dev_class_e class, int port) {
    uint32_t devItemNum = __devRegTable_end - __devRegTable_start;
    devItem_t * devRegTable = __devRegTable_start;
    uint32_t itemIdx = 0;

    for(; itemIdx < devItemNum; itemIdx++) {
        if(devRegTable[itemIdx].class == class && devRegTable[itemIdx].port == port)
            return devRegTable[itemIdx].handle;
    }

    return 0;
}